package com.butterfly.hotkey.client.core;

import cn.hutool.core.date.SystemClock;
import com.butterfly.hotkey.client.core.config.AppClientProperties;
import com.butterfly.hotkey.client.core.service.HotkeyCacheService;
import com.butterfly.hotkey.client.core.service.KeyCollectorService;
import com.butterfly.hotkey.core.cache.ICacheManager;
import com.butterfly.hotkey.core.constant.Constants;
import com.butterfly.hotkey.core.enums.CacheDurationEnum;
import com.butterfly.hotkey.core.model.HotKeyModel;
import com.butterfly.hotkey.core.model.KeyModel;
import com.butterfly.hotkey.core.model.ValueModel;
import com.butterfly.hotkey.core.rule.ClientKeyRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.LongAdder;

import static com.butterfly.hotkey.core.constant.NumConst.ONE;

/**
 * @author butterfly
 * @version 1.0
 */
public class HotkeyStore {

    private static final Logger log = LoggerFactory.getLogger(Constants.CLIENT);

    private final AppClientProperties clientProperties;

    private final KeyCollectorService keyCollectorService;

    private final HotkeyCacheService cacheService;

    public HotkeyStore(AppClientProperties clientProperties,
                       KeyCollectorService keyCollectorService,
                       HotkeyCacheService cacheService) {
        this.clientProperties = clientProperties;
        this.keyCollectorService = keyCollectorService;
        this.cacheService = cacheService;
    }

    /**
     * 判断是否是hotkey
     */
    public boolean isHotKey(String key) {
        try {
            ClientKeyRule clientKeyRule = clientProperties.findKeyRule(key);
            // 不在探测key范围内，不处理
            if (clientKeyRule == null) {
                return false;
            }

            ValueModel cacheValue = getCacheValue(key);
            // 已经在本地缓存中存在，认为是hot key
            boolean isHot = cacheValue != null;

            // 没hot或者快过期了，都进行推送
            if (!isHot || isNearExpire(cacheValue)) {
                keyCollectorService.access(buildHotKeyModel(key));
            }

            //统计计数
            keyCollectorService.collect(new KeyModel(key, isHot));

            return isHot;
        } catch (Exception e) {
            log.error("check is hotKey error", e);
        }
        return false;
    }

    /**
     * 获取本地缓存的数据
     *
     * @param key key
     * @return KeyValue
     */
    public KeyValue getValue(String key) {
        try {
            ClientKeyRule clientKeyRule = clientProperties.findKeyRule(key);

            // 不在探测key范围内，不处理
            if (clientKeyRule == null) {
                return KeyValue.nullValue(false);
            }

            ValueModel cacheValue = getCacheValue(key);
            // 已经在本地缓存中存在，认为是hot key
            boolean isHot = cacheValue != null;

            // 没hot或者快过期了，都进行推送
            if (!isHot || isNearExpire(cacheValue)) {
                keyCollectorService.access(buildHotKeyModel(key));
            }

            //统计计数
            keyCollectorService.collect(new KeyModel(key, isHot));

            if (isHot) {
                Object value = cacheValue.getValue();
                //如果是默认值，也返回null。这种情况存在于系统启动时初始化缓存数据
                if (Constants.MAGIC_NUMBER.equals(value)) {
                    return KeyValue.nullValue(true);
                } else {
                    return KeyValue.hotKey(cacheValue.getValue());
                }
            }
        } catch (Exception e) {
            log.error("get hotkey value error", e);
        }

        return KeyValue.nullValue(false);
    }

    /**
     * 删除key缓存
     * 因为是本地缓存，其余client示例也需删除缓存
     */
    public void removeAndPush(String key) {
        remove(key);

        // 通知其他client实例也删除本地缓存
        // 1.推送worker 2. worker推送给其他client
        // TODO
    }

    /**
     * 直接删除key缓存
     *
     * @param key key
     */
    public void remove(String key) {
        // 删除本地缓存
        ICacheManager cacheManager = getCacheManager(key);
        if (cacheManager != null) {
            cacheManager.invalidate(key);
        }

    }

    /**
     * 缓存hotkey，标识key已hot
     *
     * @param hotkey hotkey
     */
    public void cacheHotkey(String hotkey, Object value) {
        ClientKeyRule clientKeyRule = clientProperties.findKeyRule(hotkey);
        // 不在探测key范围内，不处理
        if (clientKeyRule == null) {

            // TODO 抽取方法
            log.warn("not valid cache hotkey:{}, can not find key rule", hotkey);
            return;
        }

        ICacheManager cacheManager = getCacheManager(hotkey);
        if (cacheManager == null) {
            log.warn("not valid cache hotkey:{}, can not find cache manager", hotkey);
            return;
        }

        // 保存缓存信息
        String duration = clientKeyRule.getDuration();
        int seconds = CacheDurationEnum.getSeconds(duration);
        ValueModel valueModel = ValueModel.of(value, seconds);
        cacheManager.put(hotkey, valueModel);
    }

    /**
     * 缓存hotkey，标识key已hot
     *
     * @param hotkey hotkey
     */
    public void markHotkey(String hotkey) {
        ClientKeyRule clientKeyRule = clientProperties.findKeyRule(hotkey);
        // 不在探测key范围内，不处理
        if (clientKeyRule == null) {
            log.warn("not valid cache hotkey:{}, can not find key rule", hotkey);
            return;
        }

        ICacheManager cacheManager = getCacheManager(hotkey);
        if (cacheManager == null) {
            log.warn("not valid cache hotkey:{}, can not find cache manager", hotkey);
            return;
        }

        // 保存缓存信息
        String duration = clientKeyRule.getDuration();
        int seconds = CacheDurationEnum.getSeconds(duration);
        ValueModel valueModel = ValueModel.of(Constants.MAGIC_NUMBER, seconds);
        cacheManager.put(hotkey, valueModel);
    }

    private HotKeyModel buildHotKeyModel(String key) {
        LongAdder adderCnt = new LongAdder();
        adderCnt.add(ONE);

        HotKeyModel hotKeyModel = new HotKeyModel();
        hotKeyModel.setCount(adderCnt);
        hotKeyModel.setAppName(clientProperties.getAppName());
        hotKeyModel.setKey(key);

        return hotKeyModel;
    }


    /**
     * 是否临近过期
     */
    private boolean isNearExpire(ValueModel valueModel) {
        if (valueModel == null) {
            return true;
        }

        long createTime = valueModel.getCreateTime();
        int expireSec = valueModel.getExpireSec();
        long now = SystemClock.now();
        long expires = createTime + expireSec * 1000L - now;

        //判断是否过期时间小于2秒，小于2秒的话也发送
        long nearSecs = Math.max(clientProperties.getWarmSecs(), 2L);
        return expires <= nearSecs * 1000L;
    }

    /**
     * 仅获取value，如果不存在也不上报热key
     */
    private ICacheManager getCacheManager(String key) {
        ClientKeyRule clientKeyRule = clientProperties.findKeyRule(key);

        // 不在探测key范围内，不处理
        if (clientKeyRule == null) {
            return null;
        }
        return cacheService.getCache(clientKeyRule.getDuration());
    }


    private ValueModel getCacheValue(String key) {
        ICacheManager cacheManager = getCacheManager(key);
        if (cacheManager == null) {
            return null;
        }
        return cacheManager.get(key);
    }


}
